#ifndef A8_SOCKETITEM_HPP
#define A8_SOCKETITEM_HPP

#include <a8/netbase.hpp>

namespace a8
{

	class ClientSocketItem
	{
	public:
		hlist_node mHlistEntry;
		list_head mListEntry;
		unsigned short mSocketHandle;	
		unsigned long mSAddr;
		std::string mRemoteAddress;
		int mRemotePort;
		unsigned long mActiveTick;
		time_t mCreateTime;
		bool mIsActivite;
		void *mOwnerListener;
		void (*mFreeClientWithNoLock)(void*, ClientSocketItem*);
		void (*mLockClients)(void*);
		void (*mUnLockClients)(void*);
#ifdef WIN32
		IocpOverLapped mOverLappedRecv;
		IocpOverLapped mOverLappedSend;
#endif
#ifdef WIN32
		HANDLE mhIocp;
#endif
#ifdef __linux__
		int mhEpoll;
#endif
#if defined(__FREEBSD__) || defined(__APPLE__)
		int mhKqueue;
#endif
	  
		ClientSocketItem():
			mSocket(INVALID_SOCKET), 
			mRemotePort(0), 
			mActiveTick(0),
			mTopNode(NULL), 
			mBotNode(NULL), 
			mWorkNode(NULL), 
			mSocketHandle(0),
			mOwnerListener(NULL),
			mLockClients(NULL),
			mUnLockClients(NULL),
			mFreeClientWithNoLock(NULL),
			mCreateTime(0),
			mIsActivite(false),
			mSAddr(0)
		{
#ifdef WIN32
			mhIocp = 0;
#endif
#ifdef __linux__
			mhEpoll = 0;
#endif
#if defined(__FREEBSD__) || defined(__APPLE__)
			mhKqueue = 0;
#endif
		}
	
		virtual ~ClientSocketItem()
		{
			Close();
			ClearSendBuff();
		}
	
		SOCKET Socket()
		{
			return mSocket;
		}

		void SetSocket(SOCKET sock)
		{
			mSocket = sock;
		}
		
		virtual void Initialize()
		{
			mSocket = INVALID_SOCKET;
			mRemoteAddress = "";
			mRemotePort = 0;
			mActiveTick = 0;
			mTopNode = NULL;
			mBotNode = NULL;
			mWorkNode = NULL;
			mSocketHandle = 0;
			INIT_HLIST_NODE(&mHlistEntry);
			INIT_LIST_HEAD(&mListEntry);
#ifdef WIN32
			memset(&mOverLappedRecv, 0, sizeof(mOverLappedRecv));
			memset(&mOverLappedSend, 0, sizeof(mOverLappedSend));
			mOverLappedRecv.mOpCode = IOCP_RECV;
			mOverLappedSend.mOpCode = IOCP_SEND;
#endif
		}

		virtual void Finalize()
		{
			ClearSendBuff();
		}

		void _ForceClose()
		{
			if(mSocket != INVALID_SOCKET){
				SOCKET oldsocket = mSocket;
				closesocket(mSocket);
				mSocket = INVALID_SOCKET;							
#ifdef __linux__
				struct epoll_event ev;
				epoll_ctl(mhEpoll, EPOLL_CTL_DEL, oldsocket, &ev);
				mFreeClientWithNoLock(mOwnerListener, this);
#endif
#ifdef WIN32
				PostQueuedCompletionStatus(mhIocp, 0, (ULONG_PTR)this, (LPOVERLAPPED)DISCONNECT_FLAG);
#endif
			}
		}

		virtual void OnError(int) {}
		virtual void OnConnect() {}
		virtual void OnDisConnect() {}
		virtual void OnSocketRead(char*, unsigned int){}

		void SendBuff(const char* buff, unsigned int bufflen)
		{
			if(mSocket == INVALID_SOCKET){
				return;
			}
			if (bufflen > 0){
				SendQueueNode* p = (SendQueueNode*)malloc(sizeof(SendQueueNode));
				memset(p, 0, sizeof(SendQueueNode));
				p->mBuff = (char*)malloc(bufflen);
				memmove(p->mBuff, buff, bufflen);
				p->mBuffLen = bufflen;							
				mSendBufferMutex.Lock();
				if(mWorkNode == NULL && mTopNode == NULL){
					mWorkNode = p;
					AsyncSend();
				}else{
					if (mBotNode){
						mBotNode->mNext = p;
						mBotNode = p;
					}else{
						mTopNode = p;
						mBotNode = p;
					}					
				}
				mSendBufferMutex.UnLock();
			}
		}

#ifdef WIN32
		void DoClientRecv(unsigned int completeBytes)
#else
		void DoClientRecv()
#endif
		{
			if(mSocket == INVALID_SOCKET){
				return;
			}
#ifdef WIN32	
			OnSocketRead(mOverLappedRecv.mWsaBuf.buf, completeBytes);
			if(mSocket != INVALID_SOCKET){
				PostRecv();
			}
#else
			char recvbuf[MAX_RECV_BUFFERSIZE];
			while(true){
				int ret = ::recv(mSocket, recvbuf, MAX_RECV_BUFFERSIZE, 0);
				if(ret < 0){
					if(errno != EAGAIN){
						Close();
						return;
					}
					break;
				}else if(ret == 0){
					Close();
					return;
				}else{
					OnSocketRead(recvbuf, ret);
					if(ret < MAX_RECV_BUFFERSIZE){
						break;
					}
				}
			}
#endif
		}

#ifdef WIN32
		void DoClientSend(int completedBytes)
#else
		void DoClientSend()
#endif
		{
			if(mSocket == INVALID_SOCKET){
				return;
			}
#ifdef WIN32
			int remainlen = mOverLappedSend.mWsaBuf.len - completedBytes;
			if(remainlen > 0){
				memmove(mOverLappedSend.mBuffer,
						mOverLappedSend.mBuffer + completedBytes,
						completedBytes);
			}
			mOverLappedSend.mWsaBuf.len = remainlen < 0 ? 0 : remainlen;
#endif
			mSendBufferMutex.Lock();
			if (!mWorkNode){
				mWorkNode = mTopNode;
				mTopNode = NULL;
				mBotNode = NULL;
			}			
			if (mWorkNode){
				AsyncSend();
			}
			mSendBufferMutex.UnLock();
		}

#ifdef WIN32
		void PostRecv()
		{
			DWORD dwFlags = 0;
			DWORD dwThancferred = 0;
			mOverLappedRecv.mSocketHandle = mSocketHandle;
			mOverLappedRecv.mWsaBuf.buf = mOverLappedRecv.mBuffer;
			mOverLappedRecv.mWsaBuf.len = MAX_IOCP_BUF_SIZE;
			memset(&mOverLappedRecv.mOverLapped, 0, sizeof(mOverLappedRecv.mOverLapped));
			int ret = WSARecv(mSocket, 
							  &mOverLappedRecv.mWsaBuf, 
							  1, 
							  &dwThancferred, 
							  &dwFlags, 
							  &mOverLappedRecv.mOverLapped, 
							  NULL);
			if(ret == SOCKET_ERROR){
				int errCode = WSAGetLastError();
				if(errCode != ERROR_IO_PENDING){
					if(mSocket != INVALID_SOCKET){
						Close();
					}
				}
			}
		}

		void PostSend()
		{
			DWORD dwFlags = 0;
			DWORD dwNumberOfBytesSent = 0;
			mOverLappedSend.mSocketHandle = mSocketHandle;
			mOverLappedSend.mWsaBuf.buf = mOverLappedSend.mBuffer;
			//mOverLappedSend.mWsaBuf.len = sendlen;		
			int ret = ::WSASend(mSocket, 
								&mOverLappedSend.mWsaBuf, 
								1, 
								&dwNumberOfBytesSent, 
								dwFlags, 
								&mOverLappedSend.mOverLapped, 
								NULL);
			if(ret == SOCKET_ERROR){
				int errCode = WSAGetLastError();
				if(errCode != ERROR_IO_PENDING){
					if(mSocket != INVALID_SOCKET){
						Close();
					}
				}
			}
		}
#endif

	private:

		void Close()
		{
			if(mSocket != INVALID_SOCKET){
				mLockClients(mOwnerListener);
				_ForceClose();
				mUnLockClients(mOwnerListener);
			}
			OnDisConnect();
		}

		void ClearSendBuff()
		{
			SendQueueNode *pTopNode  = NULL;
			SendQueueNode *pWorkNode = NULL;
			mSendBufferMutex.Lock();
			pTopNode = mTopNode;
			pWorkNode = mWorkNode;
			mTopNode = NULL;
			mBotNode = NULL;
			mWorkNode = NULL;
			mSendBufferMutex.UnLock();

			SendQueueNode *pdelnode = NULL;
			while(pTopNode){
				pdelnode = pTopNode;
				pTopNode = pTopNode->mNext;
				if(pdelnode->mBuff){
					free(pdelnode->mBuff);
				}
				free(pdelnode);
			}
			while(pWorkNode){
				pdelnode = pWorkNode;
				pWorkNode = pWorkNode->mNext;
				if(pdelnode->mBuff){
					free(pdelnode->mBuff);
				}
				free(pdelnode);
			}
		}

		void AsyncSend()
		{
#ifdef WIN32
			while(mWorkNode){
				int datalen = mWorkNode->mBuffLen - mWorkNode->mSentBytes;
				int remainLen = MAX_IOCP_BUF_SIZE - mOverLappedSend.mWsaBuf.len;
				if(datalen > remainLen){ 
					memmove(mOverLappedSend.mBuffer + mOverLappedSend.mWsaBuf.len,
							mWorkNode->mBuff + mWorkNode->mSentBytes, 
							remainLen);
					mOverLappedSend.mWsaBuf.len = MAX_IOCP_BUF_SIZE;
					mWorkNode->mSentBytes += remainLen;
					break;
				}else{
					memmove(mOverLappedSend.mBuffer + mOverLappedSend.mWsaBuf.len,
							mWorkNode->mBuff + mWorkNode->mSentBytes, 
							datalen);
					mOverLappedSend.mWsaBuf.len += datalen;
					SendQueueNode *pdelnode = mWorkNode;
					mWorkNode = mWorkNode->mNext;
					free(pdelnode->mBuff);
					free(pdelnode);
				}
			}
			if(mOverLappedSend.mWsaBuf.len > 0){
				PostSend();
			}
			mBoSending = mOverLappedSend.mWsaBuf.len > 0;
#else
			while(mWorkNode){
				int sentbytes = ::send(mSocket, 
									   mWorkNode->mBuff + mWorkNode->mSentBytes, 
									   mWorkNode->mBuffLen - mWorkNode->mSentBytes, 
									   0);
				if(sentbytes <= 0){
					break;
				}
				mWorkNode->mSentBytes += sentbytes;
				if(mWorkNode->mSentBytes >= mWorkNode->mBuffLen){
					SendQueueNode *pdelnode = mWorkNode;
					SendQueueNode *nextnode = mWorkNode->mNext;
					mWorkNode = mWorkNode->mNext; //!!!!要处理重入问题
					free(pdelnode->mBuff);
					free(pdelnode);
					if(!nextnode){
						break;
					}
				}
			}
#endif
		}
		
	private:
		SOCKET mSocket;  
		SendQueueNode *mTopNode;
		SendQueueNode *mBotNode;
		SendQueueNode *mWorkNode;
		a8::sync::Mutex mSendBufferMutex;	
	};

}

#endif
